/*
 *  Copyright (c) WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package luca.sample.rbac;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.wso2.balana.Balana;
import org.wso2.balana.PDP;
import org.wso2.balana.PDPConfig;
import org.wso2.balana.ParsingException;
import org.wso2.balana.ctx.AbstractResult;
import org.wso2.balana.ctx.AttributeAssignment;
import org.wso2.balana.ctx.ResponseCtx;
import org.wso2.balana.finder.AttributeFinder;
import org.wso2.balana.finder.AttributeFinderModule;
import org.wso2.balana.finder.PolicyFinder;
import org.wso2.balana.finder.PolicyFinderModule;
import org.wso2.balana.finder.impl.FileBasedPolicyFinderModule;
import org.wso2.balana.finder.impl.FileBasedPolicyIdFinderModule;
import org.wso2.balana.xacml3.Advice;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;

import java.io.File;
import java.io.IOException;
import java.util.*;

public class RBACAccessControl {

	private static Balana balana;
	private static final String[] actionNames = { "read", "write" };

	public static void main(String[] args) {

		Scanner console = new Scanner(System.in);

		initBalana();

		System.out.println("Insert your id: ");
		String user_id = console.next();

		System.out.println("Insert the patient id");
		String patient_id = console.next();

		String promptAction = "Choose one action:\n1. Read\n2. Write";
		System.out.println(promptAction);
		int action = console.nextInt() - 1;

		String request = createXACMLRequest(user_id, patient_id, action);
		// String request = createXACMLRequest("bob", "Food", 2, 40);
		PDP pdp = getPDPNewInstance();

		System.out
				.println("\n======================== XACML Request ====================");
		System.out.println(request);
		System.out
				.println("===========================================================");

		String response = pdp.evaluate(request);

		System.out
				.println("\n======================== XACML Response ===================");
		System.out.println(response);
		System.out
				.println("===========================================================");

		try {
			ResponseCtx responseCtx = ResponseCtx
					.getInstance(getXacmlResponse(response));
			AbstractResult result = responseCtx.getResults().iterator().next();
			if (AbstractResult.DECISION_PERMIT == result.getDecision()) {
				System.out.println("\n" + user_id
						+ " is authorized to perform this action\n\n");
			} else {
				System.out.println("\n" + user_id
						+ " is NOT authorized to perform this action\n");
				List<Advice> advices = result.getAdvices();
				for (Advice advice : advices) {
					List<AttributeAssignment> assignments = advice
							.getAssignments();
					for (AttributeAssignment assignment : assignments) {
						System.out.println("Advice :  "
								+ assignment.getContent() + "\n\n");
					}
				}
			}
		} catch (ParsingException e) {
			e.printStackTrace();
		}

	}

	private static void initBalana() {
		// create default instance of Balana
		balana = Balana.getInstance();
	}

	/**
	 * Returns a new PDP instance with new XACML policies
	 * 
	 * @return a PDP instance
	 */
	private static PDP getPDPNewInstance() {

		PDPConfig pdpConfig = balana.getPdpConfig();
		Set<PolicyFinderModule> pFinderModules = new HashSet<PolicyFinderModule>();

		// instantiate top-level policyFinderModule
		Set<String> topLevelDirs = new HashSet<String>();
		topLevelDirs.add("sample/rbac/top-level-policies");
		pFinderModules.add(new FileBasedPolicyFinderModule(topLevelDirs));

		// instantiate other policies policyFinderModule
		Set<String> otherDirs = new HashSet<String>();
		otherDirs.add("sample/rbac/others-policies");
		pFinderModules.add(new FileBasedPolicyIdFinderModule(otherDirs));

		PolicyFinder pFinder = new PolicyFinder();
		pFinder.setModules(pFinderModules);

		// registering new attribute finder. so default PDPConfig is needed to
		// change
		AttributeFinder attributeFinder = new AttributeFinder();
		List<AttributeFinderModule> finderModules = new ArrayList<AttributeFinderModule>();
		// attributeFinder.getModules();
		finderModules.add(new RBACSubjectAttributeFinderModule());
		// finderModules.add(new RBACResourceAttributeFinderModule());
		attributeFinder.setModules(finderModules);

		return new PDP(new PDPConfig(attributeFinder, pFinder, null, true));
	}

	/**
	 * Creates DOM representation of the XACML request
	 * 
	 * @param response
	 *            XACML request as a String object
	 * @return XACML request as a DOM element
	 */
	public static Element getXacmlResponse(String response) {

		ByteArrayInputStream inputStream;
		DocumentBuilderFactory dbf;
		Document doc;

		inputStream = new ByteArrayInputStream(response.getBytes());
		dbf = DocumentBuilderFactory.newInstance();
		dbf.setNamespaceAware(true);

		try {
			doc = dbf.newDocumentBuilder().parse(inputStream);
		} catch (Exception e) {
			System.err
					.println("DOM of request element can not be created from String");
			return null;
		} finally {
			try {
				inputStream.close();
			} catch (IOException e) {
				System.err
						.println("Error in closing input stream of XACML response");
			}
		}
		return doc.getDocumentElement();
	}

	public static String createXACMLRequest(String userName, String resource,
			int action) {

		return "<Request xmlns=\"urn:oasis:names:tc:xacml:3.0:core:schema:wd-17\" CombinedDecision=\"false\" ReturnPolicyIdList=\"false\">\n"
				+ "<Attributes Category=\"urn:oasis:names:tc:xacml:3.0:attribute-category:action\">\n"
				+ "<Attribute AttributeId=\"urn:oasis:names:tc:xacml:1.0:action:action-id\" IncludeInResult=\"false\">\n"
				+ "<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">"
				+ actionNames[action]
				+ "</AttributeValue>\n"
				+ "</Attribute>\n"
				+ "</Attributes>\n"
				+ "<Attributes Category=\"urn:oasis:names:tc:xacml:1.0:subject-category:access-subject\">\n"
				+ "<Attribute AttributeId=\"urn:oasis:names:tc:xacml:1.0:subject:subject-id\" IncludeInResult=\"false\">\n"
				+ "<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">"
				+ userName
				+ "</AttributeValue>\n"
				+ "</Attribute>\n"
				+ "</Attributes>\n"
				+ "<Attributes Category=\"urn:oasis:names:tc:xacml:3.0:attribute-category:resource\">\n"
				+ "<Attribute AttributeId=\"urn:oasis:names:tc:xacml:1.0:resource:resource-id\" IncludeInResult=\"false\">\n"
				+ "<AttributeValue DataType=\"http://www.w3.org/2001/XMLSchema#string\">"
				+ resource
				+ "</AttributeValue>\n"
				+ "</Attribute>\n"
				+ "</Attributes>\n" + "</Request>";

	}
}
